package cryptage;

import DesInterface.DesPanel;

/**
 * Classe de chifrement et de dechiffrement suivant le modele DES
 * @author marie, margaux
 */
public class Cryptage {
	
	//index
	private int ind = 0;
    private String binDecipher;
    private String binCipher;
    
    /**
     * la permutation
     */
    private  static Permutation p;
    
    /**
     * La cle de chiffrement
     */
    private String keyword;
    
    private int index_chooser = 0;
    
    /**
     * Le texte en clair
     */
    private String plainText;
    
    private static int index = 0;
    
    /**
     * La cle de la permutation inverse de 48 bits
     */
    private static int [] reversedkey = new int[48];
    
    /**
     * La cle (string) de la permutation inverse 
     */
    private static String key_reverse ;
    
    /**
     * Instance de GenerationClef
     */
    private static GenerationClef key = new GenerationClef();
    
    /**
     * Vecteur contenant la concatenation des parties gauche et droite du texte
     */
    private static int block64[] = new int[64];
    private static int[] num_Left = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};
    private static int ch[] = new int[8];
    private static int newBlock64[] = new int[64];
    private static int[] aft_XOR_fuc = new int[32];
    private static String[][] aft_XOR_fucS = new String[4][8];
    private static int[] after_SBox = new int[32];
    private static int[] c_D = new int[56];
    private static int[] XOR_Out = new int[48];
    
    /**
     * Cle chiffree pour le chiffrement de 64 bits
     */
    private static int key_in[] = new int[64];
    
    /**
     * Cle finale de 48 bits
     */
    private static int final_key[] = new int[48];
    
    /**
     * Cle de sortie de 56 bits
     */
    private static int key_out[] = new int[56];
    
    /**
     * Partie Gauche du texte a chiffrer ou a dechiffrer de 32 bits
     */
    private static int[] left = new int[32];
    
    /**
     * Partie Droite du texte a chiffrer ou a dechiffrer de 32 bits
     */
    private static int[] right = new int[32];
    
    /**
     * Partie Droite du texte en sortie de 48 bits
     */
    private static int[] right_out = new int[48];
    
    /** 
     * Vecteur de la permutation initial
     */
    private static int[] perm = new int[64];
   
    /**
     * Vecteur de la permutation finale
     */
    private static int[] perm_out = new int[64];
    
    private static int  newBlock64_[] = new int[64];
    
    /**
     * Partie gauche de la generation de la cle de 28 bits
     */
    private static int C[] = new int[28];
    
    /**
     * Partie droite de la generation de la cle de 28 bits
     */
    private static int D[] = new int[28];
    private byte[][] block = new byte[8][8];
    private String [][] blockS = new String[8][8];
    
    /**
     * Texte chiffre final
     */
    private String finalEncry;
    
    /**
     * Texte dechiffre final
     */
    private String finalDecry;
    
    /**
     * Tableau de string a deux dimensions representant la partie gauche
     */
    private String leftS[][] = new String[4][8];
    
    /**
     * Tableau de string a deux dimensions representant la partie droite
     */
    private String rightS[][] = new String[4][8];
    private String XORS[][] = new String[6][8];
    
    /**
     * Bloc de chiffrement 
     */
    private String [][] newBlock64_S = new String[8][8];
    
    
    /**
     * Constructeur : creation d'une instance de chiffrement
     * @param Plaintext le texte a chiffrer
     * @param Key la cle 
     */
    public Cryptage(String Plaintext, String Key) {        
        
        plainText=Plaintext;
        keyword=Key;        
    }
    
    /**
     * Methode permettant la verification de la longueur de la chaine de caracteres en texte clair
     * @param plain la chaine de caracteres en texte clair
     * @return le texte en clair
     */
    public String checkLenPlain(String plain) {
    	
    	// si la chaine n'est pas multiple de 8 alors on la remplie avec des *
        if (plain.length()%8 != 0) {
            int len = 8-plain.length()%8;            
            for (int i = 0; i < len; i++)
                plain = plain.concat("*");    
            
        } else {
            return plain;
        }
        return plain;
    }
    
    /**
     * Methode permettant de chiffrer le texte en clair en binaire
     * @param plain le texte en clair a chiffrer
     */
    public void doEncrpyt_Plaintext(String plain) {  
    	
    	// parcours du texte en clair et conversion du caractere en binaire
        for (int i = 0; i < 8 && i < plain.length(); i++) {
            block[i] = getBinaryBits(plain.charAt(i)); // tableau 2D [8][8]            
        }
        
        int index = 0;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
            	// mise a jour du tableau des permutations
                perm[index] = (int) block[i][j];
                blockS[i][j] = Integer.toString((int) block[i][j]);
                index++;                
            }
        }
        
        DesPanel.printArray(blockS, "Text en clair converti en binaire");
        DesPanel.stepsText.append("****************************************************************" + "\n");
    }
    
    /**
     * Methode permettant de chiffrer la cle
     */
    public void doEncrypt_keyword() {
        
        for (int i = 0; i < 8 && i < keyword.length(); i++) {
            block[i] = getBinaryBits(keyword.charAt(i));
            
            for (int j= 0; j < 8; j++) {                
                blockS[i][j] = Integer.toString((int) block[i][j]);                
            }
        }
        
        int index = 0;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
            	//mise a jour du tableau de la cle chiffree
                key_in[index] = (int) block[i][j];
                index++;                
            }
        }
        
        DesPanel.printArray(blockS, "Texte de la clef en binaire");
        DesPanel.stepsText.append("****************************************************************" + "\n");
    }
    
    /**
     * Methode permettant de convertir un entier en byte
     * @param entier l'entier a convertir sous forme de byte
     * @return le tableau de byte
     */
    public byte[] getBinaryBits(int entier) {
        byte[] bin = new byte[8];
        int tag = 1;
        
        for (int i = 0; i < 8; i++) {
            bin[7-i] = (byte) ((entier & ((tag << i)) ) >> i);
        }
        return bin;
    }
    
    /**
     * Methode permettant de segmenter le vecteur de permutation en partie gauche et droite
     * @param perm_out le vecteur de permutation
     */
    public void doSegmentation(int[] perm_out){
        int index = 0;
        
        // on recupere la partie gauche
        for (int i = 0; i < 32; i++)
            left[i] = perm_out[i];
        
        // on recupere la partie droite
        for (int i = 32; i < 64; i++) {
            right[index] = perm_out[i];            
            index++;
        }
        
        // recuperation des nombres dans des tableaux deux dimensions
        index = 0;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 8; j++) {
                leftS[i][j] = Integer.toString(left[index]);
                rightS[i][j] = Integer.toString(right[index]);
                index++;
            }
        } 
        DesPanel.stepsText.append("**************Segmentation*****************" + "\n");
        DesPanel.printArray(leftS, "Partie Gauche");
        DesPanel.printArray(rightS, "Partie Droite");        
    }
    
    /**
     * Methode permettant d'effectuer un XOR entre deux vecteurs d'entiers et d'enregistrer le resultat
     * @param side1 la premiere partie
     * @param side2 la seconde partie
     * @param result le vecteur resultat
     */
    public void XOR(int side1[], int[] side2, int[] result) {
        int index = 0;
        
        for (int i = 0; i < side1.length; i++) {
            if (side1[i] == side2[i])
                result[index] = 0;
            else
                result[index] = 1;
            index++;
        }        
    }
    
    /**
     * Remplissage du vecteur C_D correspondant a la cle generee des parties gauche et droite regroupees
     */
    public void fillC_D() {
        int index = 28;
        
        for (int i = 0; i < 28; i++)
            c_D[i] = C[i];
        
        for (int i = 0; i < 28; i++) {
            c_D[index] = D[i];
            index++;
        }
    }
    
    /**
     * Methode permettant la concatenation des parties gauche et droite en un seul vecteur
     */
    public void concatenation() {
        int index = 32;
        
        for (int i = 0; i < 32; i++)
            block64[i] = left[i];
        
        for (int i = 0; i < 32; i++) {
            block64[index] = right[i];
            index++;
        }
    }
    
    /**
     * Methode permettant d'echanger les partie gauche et droite
     */
    public void swap32() {
        int temp;
        
        // echange des parties gauche et droite
        for (int i = 0; i < 32; i++) {
            temp = left[i];
            left[i] = right[i];
            right[i] = temp;
        }
        
        // echange egalement des parties gauche et droite pour les tableaux LeftS et RightS
        ind = 0;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 8; j++) {
                leftS[i][j] = Integer.toString(left[ind]);
                rightS[i][j] = Integer.toString(right[ind]);
                ind++;
            }
        }
        
        DesPanel.printArray(leftS,"Partie Gauche");
        DesPanel.printArray(rightS,"Partie Droite");
        
        ind=0;
    }
    
    /**
     * Methode permettant de recuperer un tableau de byte depuis un tableau de bits
     * @param bits64 le tableeau de bits a convertir
     * @return le tableau de byte 
     */
    public static int[] getByteFromBits(int bits64[]) {
        int index = 0;
        
        System.out.println();
        for (int i = 0; i < 8; i++) {
            for (int j = 1; j <= 8; j++) {
	            ch[i] += (int) Math.pow(2, (8-j)) * bits64[index];
	            index++;
            }
        }
        
        return ch;
    }
    
    /**
     * Methode permettant de choisir le permuted choice (1 ou 2)
     * @param choice le choix 1 ou 2 
     */
    public void chooser(int choice) {
    	
        if (choice == 1) {
            System.out.println("Avant le decalage vers la gauche " + index);
            
            for (int j = 0; j < C.length; j++)
                System.out.print(C[j]);
            
            // decalage a gauche
            key.doOneLeftShift(C,D);
            System.out.println();
            System.out.println("Apres le decalage vers la gauche" + index);
            
            for (int j = 0; j < C.length; j++)
                System.out.print(C[j]);
            
        } else {
            // affichage de la matrice C
            for (int j = 0; j < C.length; j++)
                System.out.print(C[j]);
            
            // decalages a gauche deux fois
            key.doOneLeftShift(C,D);
            key.doOneLeftShift(C,D);
            System.out.println(" Gauche = ");
            
            for (int j = 0; j < C.length; j++)
                System.out.print(C[j]);
            
            System.out.println(" Droite = ");
            
            for (int j = 0; j < D.length; j++)
                System.out.print(D[j]);            
        }        
    }
    
    /**
     * Methode permettant de chiffrer le texte en clair avec la cle 
     */
    public void doEncryption() {
        DesPanel.stepsText.append("************************ CHIFFREMENT ***********************************" + "\n");
        
        // cryptage de la cle
        doEncrypt_keyword();
        int start = 0;
        int end = 8;
        
        // verification du texte en clair
        plainText = checkLenPlain(plainText);
        System.out.println("Texte en clair: " + plainText.substring(start, start+9));
        
        String temp;
        for (int f = 0; f < plainText.length()/8; f++) {          
            
        	// recuperation des premiers caracteres 
            temp = plainText.substring(start, end);
            
            // cryptage du texte en clair
            doEncrpyt_Plaintext(temp);            
            
            System.out.println("Le message code original : ");
            for (int i = 0; i < 64; i++) {
                if (binCipher == null)
                    binCipher = Integer.toString(perm[i]);
                else
                    binCipher += Integer.toString(perm[i]);
                System.out.print(perm[i]);
            }
            
            int Round = 1;
            
            DesPanel.stepsText.append("********* Numero de bloc *********" + (f+1) + "\n");
            System.out.println(" ");
            
            Permutation p= new Permutation();
            p.fillPermutation(); // step1 from 2D to 1D
            
            // permutation initial
            p.doPermutation(perm, perm_out); // step1 -array
            doSegmentation(perm_out); // step2 to Left and Right
                       
            
            ////////////////////////////////////////////////////////////////////////
            ///////////////////////////// generation de la cle ////////////////////////////
            
            key.fillPermutedChoice1(); // step1 -array
            System.out.println();
            DesPanel.stepsText.append("********* Generation de la cle *********" + "\n");
            
            // Permutation choice 1
            key.doPermutedChoice1(key_in, key_out); // step1            
            key.doKeySegmentation(key_out, C, D); // step2
            
            // on fait 16 rounds
            while (Round <= 16) {
            	
                DesPanel.stepsText.append("********* Numero de round ********* " + Round + "\n");
                
                // on recupere le choix 
                int chooser = num_Left[index_chooser];
                ESTable Etable = new ESTable();
                Etable.fillETable(); // step3 array from 2D to 1D
                Etable.doETable(right, right_out); // step3
                
                ////////////////////////////////////////////////////////////////////////
                System.out.println();
                chooser(chooser); // step 3 - shift
                System.out.println("Chooser = " + chooser);
                key.fillPermutedChoice2(); // step4 array
                fillC_D(); // step4 concatenation 56bits
                DesPanel.stepsText.append("********* Cle pour le numero de round ********* " + Round + "\n");
                
                // Permutation choice 2
                key.doPermutedChoice2(c_D, final_key); // step4 here the key of Round 1
                
                for (int i = 0; i < 48; i++) {
                    if (key_reverse == null)
                        key_reverse = Integer.toString(final_key[i]);
                    else
                        key_reverse += Integer.toString(final_key[i]);
                    
                }              
                ////////////////////////////////////////////////////////////////////////
                ////////////////////////////////////////////////////////////////////////
                
                DesPanel.stepsText.append("********* Partie Droite XOR avec la cle du round ********* " + Round + "\n");
                XOR(right_out,final_key,XOR_Out); // step1
                
                ind = 0;
                for (int g = 0; g < 6; g++) {
                    for (int j = 0; j < 8; j++) {
                        XORS[g][j] = Integer.toString(XOR_Out[ind]);
                        ind++;
                        
                    }
                }
                ind = 0;
                
                DesPanel.printArray(XORS, "Resultat du XOR");
                System.out.print("\nXOR : ");
                
                for (int j = 0; j < 48; j++) {
                    System.out.print(XOR_Out[j]);
                }
                
                SBox sbox = new SBox();
                sbox.doSBox(XOR_Out, after_SBox); // step2 32bits - include permutation
                System.out.print("\nS-BOX : ");
               
                for (int j = 0; j < 32; j++) {
                    System.out.print(after_SBox[j]);
                }
                
                System.out.print("\n Apres XOR  : ");
                DesPanel.stepsText.append("********* Partie Gauche XOR avec la fonction de sortie du round ******** " + Round + "\n");
                XOR(left, after_SBox, aft_XOR_fuc); // XOR
                               
           
                for (int g = 0; g < 4; g++) {
                    for (int j = 0; j < 8; j++) {
                        aft_XOR_fucS[g][j] = Integer.toString(aft_XOR_fuc[ind]);
                        ind++;
                    }
                }
                
                ind = 0;
                DesPanel.printArray(aft_XOR_fucS, "Resultat XOR");
                
                for (int j = 0; j < 32; j++) {
                    System.out.print(aft_XOR_fuc[j]);
                }                
                
                Round++;
                index++;
                index_chooser++;
                
                DesPanel.stepsText.append("********* Gauche=Droite & Droite=Gauche *********" + "\n");
                
                for (int i = 0; i < 32; i++) {
                    left[i] = right[i];
                    right[i] = aft_XOR_fuc[i];
                }
                
                ind = 0;
                for (int g = 0; g < 4; g++) {
                    for (int j = 0; j < 8; j++) {
                        leftS[g][j] = Integer.toString(right[ind]);
                        rightS[g][j] = Integer.toString(aft_XOR_fuc[ind]);
                        ind++;
                    }
                }
                
                DesPanel.printArray(leftS, "Partie Gauche");
                DesPanel.printArray(rightS, "Partie Droite");                
            }
            
            DesPanel.stepsText.append("******** Apres l'operation d'echange *********" + "\n");
            swap32();            
            concatenation();
            
            System.out.println("/nBit Swap :");
            
            for (int i = 0; i < 64; i++) {
                System.out.print(block64[i]);
            }
            
            // permutation initial inverse
            p.fillInversePermutation(); // step1 from 2D to 1D
            DesPanel.stepsText.append("********* Operation de permutation inverse dans le round *********" + (f+1) + "\n");
            p.doInversePermutation(block64,newBlock64); // step1 -array
            System.out.println(" ");
            System.out.println("/nLe code de cryptage : ");
            
            for (int i = 0; i < newBlock64.length; i++) {
                System.out.print(newBlock64[i]);
                if (finalEncry == null)
                    finalEncry = Integer.toString(newBlock64[i]);
                else
                    finalEncry += Integer.toString(newBlock64[i]);
                
            }
            start = end;
            end = end + 8;
            index_chooser = 0;
            
        }
        System.out.println("");
        System.out.println("Final : "+ finalEncry);        
    }
    
    /**
     * Methode permettant de decrypter un texte chiffre
     */
    public void doDecryption() {
        DesPanel.stepsText.append("************************ DECHIFFREMENT ***********************************" + "\n");
        
        int start = 0;
        int counter = 0;
        int end = 64;
        
        for (int f = 0; f < plainText.length()/8; f++) {
            
            int Round = 1;
            for (int h = start; h < end; h++) {
                newBlock64_[counter] = Integer.parseInt(finalEncry.substring(h, h + 1));
                counter++;
            }
            
            DesPanel.stepsText.append("********* Numero de bloc *********" + (f+1) + "\n");
            
            ind = 0;
            DesPanel.stepsText.append("********* Bloc de chiffrement dans le round ********* " + Round + "\n");
           
            for (int d = 0 ; d < 8; d++) {
                for (int j = 0; j < 8; j++) {
                    newBlock64_S[d][j] = Integer.toString(newBlock64[ind]);
                    ind++;
                }
            }
            DesPanel.printArray(newBlock64_S, "Bloc de chiffrement");
            
            p = new Permutation();
            
            // permutation initiale
            p.fillPermutation(); // step1 from 2D to 1D
            p.doPermutation(newBlock64_, perm_out);
            doSegmentation(perm_out);
            
            int adder = 48;
            
            // 16 rounds
            while (Round <= 16) {
                DesPanel.stepsText.append("********* Numero du round ********* " + Round + "\n");
                
                for (int i = 0; i < 48; i++) {
                    reversedkey[i] = Integer.parseInt(key_reverse.substring((key_reverse.length() - adder) + i, (key_reverse.length() - adder) + i + 1));
                }
                
                System.out.println();
                ESTable Etable = new ESTable();
                Etable.fillETable(); // step3 array from 2D to 1D
                Etable.doETable(right, right_out); // step3
                
                DesPanel.stepsText.append("********* Partie Droite XOR avec la cle round ********* " + Round + "\n");
                XOR(right_out, reversedkey, XOR_Out); // step1
                ind = 0;
                
                for (int g = 0; g < 6; g++) {
                    for (int j = 0; j < 8; j++) {
                        XORS[g][j] = Integer.toString(XOR_Out[ind]);
                        ind++;                        
                    }
                }
                
                DesPanel.printArray(XORS, "Resultat XOR");
                ind = 0;
                
                SBox sbox = new SBox();                
                sbox.doSBox(XOR_Out, after_SBox); // step2 32bits - include permutation
                DesPanel.stepsText.append("********* Partie Gauche XOR avec la fonction de sortie du round ********* " + Round + "\n");
                XOR(left, after_SBox, aft_XOR_fuc); // XOR
                
                for (int g = 0; g < 4; g++) {
                    for (int j = 0; j < 8; j++) {
                        aft_XOR_fucS[g][j] = Integer.toString(aft_XOR_fuc[ind]);
                        ind++;
                    }
                }
                
                ind = 0;
                DesPanel.printArray(aft_XOR_fucS, "Resultat XOR");
                
                adder = adder + 48;
                Round++;
                
                DesPanel.stepsText.append("********* Gauche = Droite & Droite = Gauche *********" + "\n");
                
                for (int i = 0; i < 32; i++) {
                    left[i] = right[i];
                    right[i] = aft_XOR_fuc[i];
                }                
            }
            
            ind = 0;
            for (int g = 0; g < 4; g++) {
                for (int j = 0; j < 8; j++) {
                    leftS[g][j] = Integer.toString(right[ind]);
                    rightS[g][j] = Integer.toString(aft_XOR_fuc[ind]);
                    ind++;                    
                }
            }
            
            DesPanel.printArray(leftS, "Partie Gauche");
            DesPanel.printArray(rightS, "Partie Droite");
            
            DesPanel.stepsText.append("******** Apres l'operation d'echange ********* " + "\n");
            swap32();            
            concatenation();
            
            System.out.println("/nBit Swap : ");
            for (int i = 0; i < 64; i++) {
                System.out.print(block64[i]);
            }
            
            // permutation inverse
            p.fillInversePermutation(); // step1 from 2D to 1D
            p.doInversePermutation(block64, newBlock64); // step1 - array
            
            System.out.println("Le code de decryptage : ");
            
            for (int i = 0; i < 64; i++) {
                if (binDecipher == null)
                    binDecipher = Integer.toString(newBlock64[i]);
                else
                    binDecipher += Integer.toString(newBlock64[i]);
                System.out.print(newBlock64[i]);
            }
            
            System.out.println(" ");
            ch = getByteFromBits(newBlock64);            
            
            for (int i = 0; i < 8; i++) {
                System.out.print((char) ch[i]);
                if (finalDecry == null)
                    finalDecry = Character.toString((char) ch[i]);
                else
                    finalDecry += Character.toString((char) ch[i]);
            }
            
            for (int i = 0; i < 8; i++)
                ch[i] = 0;
            
            start = end;
            end = end + 64;
            counter = 0;
        }
    }
    
    /**
     * 
     * @return binDecipher
     */
    public String getBinDecipher() {
        return binDecipher;
    }
    
    /**
     * 
     * @return final encryption
     */
    public String getEncryption() {
        return finalEncry;
    }
    
    /**
     * 
     * @return final decryption
     */
    public String getDecryption() {
        return finalDecry;
    }
    
    /**
     * 
     * @return binCipher
     */
    public String getBinCipher() {
        return binCipher;
    }  
}